<?php

declare(strict_types=1);
/**
 * This file is part of habo_new_media.
 *
 * @link     https://www.berlen.cn/
 * @contact  tech@berlen.cn
 * @license  http://gitlab.rehou.cn/habo/habo_new_media/-/blob/master/README.md
 */

namespace app\service\batch\kuaishou;

use app\enum\BatchProjectEnum;
use app\enum\CommonEnum;
use app\enum\KuaiShouEnum;
use app\model\BatchProjectContentModel;
use app\model\BatchProjectModel;
use app\service\batch\BatchService;
use app\service\client\AccountService;
use app\service\kuaishou\AdvertiserService;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\Exception;
use think\Model;

/**
 * 智能化2.0项目搭建推送
 */
final class CreateProjectService extends BatchService
{
    /**
     * 创建计划.
     * @param BatchProjectContentModel $model 内容模型
     */
    public static function projectCampaignCreate(BatchProjectContentModel $model): bool
    {
        try {
            if ($model->getAttr('content_level') != BatchProjectEnum::LEVEL_CAMPAIGN) {
                throw new Exception('结构体层级有误');
            }
            // 如果已经搭建已经成功，有返回id，说明此条是修改状态失败，只需执行修改状态
            $resultData = ['status' => BatchProjectEnum::PROJECT_STATUS_CONTENT_SUCCESS];
            $content = json_decode($model->getAttr('content'), true);
            $ip = self::getBatchIp($model->getAttr('batch_project_id'));
            $campaignId = $model->getAttr('return_api_id');
            if (! $model->getAttr('return_api_id')) {
                // 获取结构体
                $res = self::campaignCreate($model->getAttr('advertiser_id'), $content, $ip);
                if (! $res) {
                    // 获取下级组id，并进redis
                    throw new Exception(AdvertiserService::getError(0));
                }
                $resultData['return_api_id'] = $res['data']['campaign_id'];
                $campaignId = $res['data']['campaign_id'];
            }
            $model->setAttrs($resultData);
            $model->save();
            // 修改计划暂停状态
            if (empty($content['is_open'])) {
                // 随机停
                usleep(rand(600000, 1200000));
                $statusRes = self::campaignStatus($model->getAttr('advertiser_id'), strval($campaignId), $ip);
                if (! $statusRes) {
                    throw new Exception(self::getError(0));
                }
            }
            // 获取下级组id，并进redis
            self::pushKsUnitList(self::getNextId($model->getKey()));
            return true;
        } catch (\Exception $e) {
            dump($e);
            self::setFalse($model, $e->getMessage());
            self::pushKsUnitList(self::getNextId($model->getKey()));
            return self::setError($e->getMessage());
        }
    }

    /**
     * 创建组.
     * @param BatchProjectContentModel $model 内容模型
     * @throws DataNotFoundException
     * @throws DbException
     * @throws Exception
     * @throws ModelNotFoundException
     */
    public static function projectUnitCreate(BatchProjectContentModel $model): bool
    {
        if ($model->getAttr('content_level') != BatchProjectEnum::LEVEL_UNIT) {
            return self::setError('结构体层级有误');
        }
        // 获取结构体
        $content = json_decode($model->getAttr('content'), true);
        // 获取计划id
        $campaign = self::getContent($model->getAttr('parent_id'));
        if (! $campaign) {
            return self::setError('获取广告计划失败');
        }
        if (! $campaign->getAttr('return_api_id') && $campaign->getAttr('status') == BatchProjectEnum::PROJECT_STATUS_CONTENT_FALSE) {
            return self::setError('上级搭建失败');
        }
        if ($campaign->getAttr('status') != BatchProjectEnum::STATUS_SUCCESS) {
            return self::setError('广告计划状态有误');
        }
        $content['campaign_id'] = $campaign->getAttr('return_api_id');
        if (! empty($content['begin_time']) && $content['begin_time'] < date('Y-m-d')) {
            $content['begin_time'] = date('Y-m-d');
        }
        $res = self::unitCreate($model->getAttr('advertiser_id'), $content, self::getBatchIp($model->getAttr('batch_project_id')));
        if (! $res) {
            return false;
        }
        // 查询是否需要走一遍修改(更正app行为，只有修改接口才行)
        if (! isset($content['target']['app_interest_ids']) && isset($content['target']['app_ids']) && $content['target']['app_ids'] == []) {
            unset($content['campaign_id']);
            $content['unit_id'] = $res['data']['unit_id'];
            if (! self::unitUpdate($model->getAttr('advertiser_id'), $content, self::getBatchIp($model->getAttr('batch_project_id')))) {
                //                return false;
                $model->setAttr('error_msg', self::getError(0));
            }
        }

        $model->setAttr('status', BatchProjectEnum::PROJECT_STATUS_CONTENT_SUCCESS);
        $model->setAttr('return_api_id', $res['data']['unit_id']);
        return $model->save();
    }

    /**
     * 创建创意.
     * @param BatchProjectContentModel $model 内容模型
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function projectCreativeCreate(BatchProjectContentModel $model): bool
    {
        try {
            if ($model->getAttr('content_level') != BatchProjectEnum::LEVEL_CREATIVE) {
                throw new Exception('结构体层级有误');
            }
            // 获取结构体
            $content = json_decode($model->getAttr('content'), true);
            // 获取计划id
            $unit = self::getContent($model->getAttr('parent_id'));
            if (! $unit) {
                throw new Exception('获取广告组失败');
            }
            if ($unit->getAttr('status') == BatchProjectEnum::STATUS_WAIT) {
                // 如果未执行，插回队尾
                self::pushKsCreativeList($model->getKey());
                return true;
            }
            if (! $unit->getAttr('return_api_id')) {
                throw new Exception('上级搭建失败');
            }
            $content['unit_id'] = $unit->getAttr('return_api_id');
            $res = self::creativeCreate($model->getAttr('advertiser_id'), $content, self::getBatchIp($model->getAttr('batch_project_id')));
            if (! $res) {
                throw new Exception(AdvertiserService::getError(0));
            }
            $model->setAttrs([
                'status' => BatchProjectEnum::PROJECT_STATUS_CONTENT_SUCCESS,
                'return_api_id' => $res['data']['creative_id'] ?? 1,
            ]);
            $model->save();
            // 判断是否全部完成，修改并修改状态
            $res = self::setProjectStatus($model->getAttr('batch_project_id'));
            if (! $res) {
                throw new Exception('主表状态修改失败');
            }

            return true;
        } catch (\Exception $e) {
            self::setFalse($model, $e->getMessage());
            self::setProjectStatus($model->getAttr('batch_project_id'));
            return self::setError($e->getMessage());
        }
    }

    /**
     * 设置失败状态.
     * @param BatchProjectContentModel $model 结构体表id
     * @param string $errorMsg 错误信息
     */
    public static function setFalse(BatchProjectContentModel $model, string $errorMsg = ''): bool
    {
        $model->setAttrs([
            'status' => BatchProjectEnum::PROJECT_STATUS_CONTENT_FALSE,
            'error_msg' => $errorMsg,
        ]);
        return $model->save();
    }

    /**
     * 设置搭建状态.
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function setProjectStatus(int $id): bool
    {
        $model = (new BatchProjectModel())->where('id', $id)->find();
        if (! $model) {
            return false;
        }
        $isOk = (new BatchProjectContentModel())->where('status', BatchProjectEnum::STATUS_WAIT)
            ->where('batch_project_id', $id)->find();
        // 是否全部执行过
        if (! $isOk) {
            // 判断是否有失败
            $isError = (new BatchProjectContentModel())->where('status', BatchProjectEnum::PROJECT_STATUS_CONTENT_FALSE)
                ->where('batch_project_id', $id)->find();
            if ($isError) {
                // 部分成功
                $model->setAttr('status', BatchProjectEnum::STATUS_FAIL);
            } else {
                // 全部成功
                $model->setAttr('status', BatchProjectEnum::STATUS_SUCCESS);
            }
            return $model->save();
        }
        return true;
    }

    /**
     * 推送广告计划.
     * @param string $advertiserId 账户id
     * @param array $content 结构体
     * @param string $ip 搭建人ip
     * @return bool|mixed|string
     */
    private static function campaignCreate(string $advertiserId, array $content = [], string $ip = '')
    {
        $token = AccountService::getToken(CommonEnum::KS_MEDIA, $advertiserId);
        if (! $token) {
            return self::setError('token获取失败');
        }
        $header = [
            'Access-Token' => $token,
            'Content-Type' => 'application/json',
            'K-RealUser-IP' => $ip,
        ];
        return AdvertiserService::campaignCreate($header, $content);
    }

    /**
     * 修改广告计划状态.
     * @param string $advertiserId 账户id
     * @param string $campaignId 计划id
     * @param string $ip 搭建人ip
     * @return bool|mixed|string
     */
    private static function campaignStatus(string $advertiserId, string $campaignId = '', string $ip = '')
    {
        $token = AccountService::getToken(CommonEnum::KS_MEDIA, $advertiserId);
        if (! $token) {
            return self::setError('token获取失败');
        }
        $header = [
            'Access-Token' => $token,
            'Content-Type' => 'application/json',
            'K-RealUser-IP' => $ip,
        ];
        $params = [
            'advertiser_id' => $advertiserId,
            'campaign_id' => $campaignId,
            'put_status' => 2,
        ];
        return AdvertiserService::campaignStatus($header, $params);
    }

    /**
     * 推送广告组.
     * @param string $advertiserId 账户id
     * @param array $content 结构体
     * @param string $ip 搭建人ip
     * @return bool|mixed|string
     */
    private static function unitCreate(string $advertiserId, array $content = [], string $ip = '')
    {
        $token = AccountService::getToken(CommonEnum::KS_MEDIA, $advertiserId);
        if (! $token) {
            return self::setError('token获取失败');
        }
        $header = [
            'Access-Token' => $token,
            'Content-Type' => 'application/json',
            'K-RealUser-IP' => $ip,
        ];
        return AdvertiserService::unitCreate($header, $content);
    }

    /**
     * 修改广告组.
     * @param string $advertiserId 账户id
     * @param array $content 结构体
     * @param string $ip 搭建人ip
     * @return bool|mixed|string
     */
    private static function unitUpdate(string $advertiserId, array $content = [], string $ip = '')
    {
        $token = AccountService::getToken(CommonEnum::KS_MEDIA, $advertiserId);
        if (! $token) {
            return self::setError('token获取失败');
        }
        $header = [
            'Access-Token' => $token,
            'Content-Type' => 'application/json',
            'K-RealUser-IP' => $ip,
        ];
        return AdvertiserService::updateUnit($header, $content);
    }

    /**
     * 推送创意计划.
     * @param string $advertiserId 账户id
     * @param array $content 结构体
     * @param string $ip 搭建人ip
     * @return bool|mixed|string
     */
    private static function creativeCreate(string $advertiserId, array $content = [], string $ip = '')
    {
        $token = AccountService::getToken(CommonEnum::KS_MEDIA, $advertiserId);
        if (! $token) {
            return self::setError('token获取失败');
        }
        $header = [
            'Access-Token' => $token,
            'Content-Type' => 'application/json',
            'K-RealUser-IP' => $ip,
        ];
        if ($content['creative_type'] == KuaiShouEnum::CREATIVE_TYPE_CUSTOM) {
            return AdvertiserService::creativeCreate($header, $content);
        }
        return AdvertiserService::programCreativeCreate($header, $content);
    }

    /**
     * 获取搭建人ip.
     * @param int $id 上级表id
     * @return mixed
     */
    private static function getBatchIp(int $id = 0)
    {
        return (new BatchProjectModel())->where('id', $id)->value('ip_address');
    }

    /**
     * 获取content.
     * @param int $id 上级表id
     * @return null|array|BatchProjectContentModel|mixed|Model
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    private static function getContent(int $id = 0)
    {
        return (new BatchProjectContentModel())->where('id', $id)->find();
    }

    /**
     * 获取下级内容id.
     * @param int $id 表id
     */
    private static function getNextId(int $id = 0): array
    {
        return (new BatchProjectContentModel())->where('parent_id', $id)->column('id');
    }
}
